/*
 * Copyright (C) 2023 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_i2c.h"
#include "hi_io.h"
#include "hi_pwm.h"
#include "ssd1306.h"

#define I2C0_IDX      (0)
#define I2C0_BAUDRATE (400*1000)

extern void OledTestEntry1(void);
extern void OledTestEntry2(void);
extern void LedTestEntry(void);
extern void BeepTestEntry(void);

extern void Ltr553TestEntry(void);
extern void Lis3dhTestEntry(void);
extern void SixAxisTestEntry(void);
extern void Aht20TestEntry(void);

extern void WifiApTestEntry(void);
extern void WifiApTestExit(void);
extern void WifiStaTestEntry(void);
extern void WifiStaTestExit(void);
extern void UnisoundTestEntry(void);
extern void UnisoundTestExit(void);
extern void NfcTestEntry(void);

extern void DispayApInfo(void);
extern void DispayStaInfo(void);
extern void DispayNfcInfo(void);

static void DispayUniInfo(void)
{
    ssd1306_Fill(Black);
    ssd1306_SetCursor(5, 5);
    ssd1306_DrawString("Unisound", Font_11x18, White);
    ssd1306_SetCursor(5, 5+18+5);
    ssd1306_DrawString("..........", Font_7x10, White);
    ssd1306_SetCursor(5, 5+18+5+10+5+10);
    ssd1306_DrawString("UP          DOWN", Font_7x10, White);
    ssd1306_UpdateScreen();
}

#define TestCaseCnt 8
int g_TestCase  = 0;  // current runnint test case
int g_TCSW      = 1;  // test case switch, when init, run test case 0 then flow to case 1

//////////////////////////////////////////////
#define BTN_INT_TYPE  IOT_INT_TYPE_LEVEL    // IOT_INT_TYPE_EDGE
enum BtnSatus
{
    BTN_INVALID = -1,
    BTN_RELEASE = 0,
    BTN_PRESS   = 1,
};
static int btn_a_st = BTN_INVALID;
static int btn_b_st = BTN_INVALID;
//static int btn_a_cnt = -1;
//static int btn_b_cnt = -1;

static void BtnA_Press(void)
{
    btn_a_st = BTN_PRESS;
}
static void BtnA_Release(void)
{
    if (btn_a_st == BTN_PRESS) {
        btn_a_st = BTN_RELEASE;
        if (g_TestCase == 0) {
            g_TestCase = TestCaseCnt - 1;
        } else {
            g_TestCase = g_TestCase - 1;
        }
        g_TCSW = 1;
    }
    printf("%s: g_TestCase[%d]\n", __func__, g_TestCase);

    IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_10,
                           BTN_INT_TYPE,
                           IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           BtnA_Press,
                           NULL);
}
static void BtnB_Press(void)
{
    btn_b_st = BTN_PRESS;
}
static void BtnB_Release(void)
{
    if (btn_b_st == BTN_PRESS) {
        btn_b_st = BTN_RELEASE;
        g_TestCase = (g_TestCase+1)%TestCaseCnt;
        g_TCSW = 1;
    }
    printf("%s: g_TestCase[%d]\n", __func__, g_TestCase);

    IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_2,
                           BTN_INT_TYPE,
                           IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           BtnB_Press,
                           NULL);
}

// [0]-process button press/release event in Maintask
// [1]-process button press/release event in ButtonTask, created in CreateButtonTask()
#define ButtonThread   (1)
void ButtonIoInit(void)
{
    // BUTTON_A inited with a BTN_INT_TYPE trigger interrupt
    IoTGpioInit(HI_IO_NAME_GPIO_10);
    hi_io_set_func(HI_IO_NAME_GPIO_10, HI_IO_FUNC_GPIO_10_GPIO);
    IoTGpioSetDir(HI_IO_NAME_GPIO_10, IOT_GPIO_DIR_IN);
    hi_io_set_pull(HI_IO_NAME_GPIO_10, HI_IO_PULL_UP);
    IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_10,
                           BTN_INT_TYPE,
                           IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           BtnA_Press,
                           NULL);

    // BUTTON_B inited with a BTN_INT_TYPE trigger interrupt
    IoTGpioInit(HI_IO_NAME_GPIO_2);
    hi_io_set_func(HI_IO_NAME_GPIO_2, HI_IO_FUNC_GPIO_10_GPIO);
    IoTGpioSetDir(HI_IO_NAME_GPIO_2, IOT_GPIO_DIR_IN);
    hi_io_set_pull(HI_IO_NAME_GPIO_2, HI_IO_PULL_UP);
    IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_2,
                           BTN_INT_TYPE,
                           IOT_GPIO_EDGE_FALL_LEVEL_LOW,
                           BtnB_Press,
                           NULL);
}

void ButtonIntAlter(void)
{
    if (btn_a_st == BTN_PRESS) {
        IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_10,
                               BTN_INT_TYPE,
                               IOT_GPIO_EDGE_RISE_LEVEL_HIGH,
                               BtnA_Release,
                               NULL);
    }
    if (btn_b_st == BTN_PRESS) {
        IoTGpioRegisterIsrFunc(HI_IO_NAME_GPIO_2,
                               BTN_INT_TYPE,
                               IOT_GPIO_EDGE_RISE_LEVEL_HIGH,
                               BtnB_Release,
                               NULL);
    }
}

#if ButtonThread
static osThreadId_t BtnTaskId = NULL;
void ButtonTask(void)
{
    printf("%s: running ...\n", __func__);
    while (1) {
        usleep(50*1000);
        ButtonIntAlter();
    }
}

osThreadId_t CreateButtonTask(void)
{
    //osPriorityAboveNormal[32], osPriorityNormal[24]
    //{.name, .attr_bits, .cb_mem, .cb_size, .stack_mem, .stack_size, .priority, .tz_module, .reserved}
    osThreadAttr_t btnAttr = {"ButtonTask", 0, NULL, 0, NULL, 1024*2, 30, 0, 0};
    osThreadId_t taskId = osThreadNew(ButtonTask, NULL, &btnAttr);

    if (taskId == NULL) {
        printf("%s: Falied to create %s!\n", __func__, btnAttr.name);
        return NULL;
    }
    return taskId;
}
#endif

static int ButtonInit(void)
{
    ButtonIoInit();
#if ButtonThread
    BtnTaskId = CreateButtonTask();
    if (BtnTaskId == NULL) {
        printf("%s: CreateButtonTask NG!", __func__);
        return -1;
    }
#endif
    return 0;
}
static int I2C0Init(void)
{
    // I2C0 inited for all components
    IoTGpioInit(HI_IO_NAME_GPIO_13);
    IoTGpioInit(HI_IO_NAME_GPIO_14);
    hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
    hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);

    return IoTI2cInit(I2C0_IDX, I2C0_BAUDRATE);
}

void MainTask_LedBeep(bool On)
{
    IoTGpioInit(HI_IO_NAME_GPIO_0);
    hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_GPIO);
    IoTGpioSetDir(HI_IO_NAME_GPIO_0, IOT_GPIO_DIR_OUT);

    IoTGpioInit(HI_IO_NAME_GPIO_5);
    hi_io_set_func(HI_IO_NAME_GPIO_5, HI_IO_FUNC_GPIO_5_PWM2_OUT);
    IoTGpioSetDir(HI_IO_NAME_GPIO_5, IOT_GPIO_DIR_OUT);
    IoTPwmInit(HI_PWM_PORT_PWM2);

    if (On) {
        IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 1);
        IoTPwmStart(HI_PWM_PORT_PWM2, 50, 4000);
    } else {
        IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 0);
        IoTPwmStop(HI_PWM_PORT_PWM2);
    }
}

static void MainTask(void)
{
    printf("%s: Begin:\n", __func__);
    hi_watchdog_disable();

    int ret = ButtonInit();
    if (ret != 0) {
        printf("%s: End. ButtonInit NG!\n", __func__);
        return;
    }
    I2C0Init();
    if (ret != 0) {
        printf("%s: End. I2C0Init NG!\n", __func__);
        return;
    }
    ssd1306_Init();

    while (1) {
        //printf("%s: g_TestCase[%d]\n", __func__, g_TestCase);
        if (!g_TCSW) {
            usleep(100000);
            continue;
        }

        // g_TCSW[1]: test case switch to g_TestCase
        switch (g_TestCase) {
        case 0:
            // 0-OLedTest
            ssd1306_Fill(White);
            ssd1306_UpdateScreen();

            // 1-LedTest + 2-BeepTest
            MainTask_LedBeep(true);
            usleep(1500*1000);
            MainTask_LedBeep(false);
            // break;          // NOTE: no break, flow to next case
        case 1:
            g_TestCase = 1;    // NOTE: [0, 1] => [1]
            DispayNfcInfo();
            NfcTestEntry();    // individual task
            break;
        case 2:
            Ltr553TestEntry();
            break;
        case 3:
            Lis3dhTestEntry();
            break;
        case 4:
            Aht20TestEntry();
            break;
        case 5:
            DispayApInfo();
            WifiApTestEntry();    // individual task
            break;
        case 6:
            DispayStaInfo();
            WifiStaTestEntry();    // individual task
            break;
        case 7:
            DispayUniInfo();
            UnisoundTestEntry();    // individual task
            break;
        default:
            break;
        }

        g_TCSW = 0;
        usleep(100*1000);
    }

    printf("%s: End.\n", __func__);
    printf("===================================================\n");
}

static void MainEntry(void)
{
    //osPriorityAboveNormal[32], osPriorityNormal[24]
    //{.name, .attr_bits, .cb_mem, .cb_size, .stack_mem, .stack_size, .priority, .tz_module, .reserved}
    osThreadAttr_t mainAttr = {"MainTask", 0, NULL, 0, NULL, 1024*16, 24, 0, 0};

    printf("\n\n");
    printf("===================================================\n");
    printf("[MainEntry] MainEntry() to create %s:\n", mainAttr.name);
    if (osThreadNew((osThreadFunc_t)MainTask, NULL, &mainAttr) == NULL) {
        printf("[MainEntry] Falied to create %s!\n", mainAttr.name);
    }
}
SYS_RUN(MainEntry);
